package com.example.spider.provider.m3u8;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.example.spider.provider.m3u8.dto.M3u8Key;
import com.example.spider.provider.m3u8.dto.M3u8MetaInfo;
import com.example.spider.provider.m3u8.dto.M3u8SpecEnum;
import com.example.spider.provider.m3u8.dto.M3u8TsPart;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * 解析 m3u8 文件
 */
public class M3u8Parser {

    public static M3u8MetaInfo parse(String m3u8Content, String m3u8Url, Set<String> forbiddenPartUrls) {
        Assert.isTrue(StrUtil.contains(m3u8Content, Constants.EXTM3U_TAG), "不是m3u8");

        M3u8MetaInfo metaInfo = new M3u8MetaInfo();
        metaInfo.setUrl(m3u8Url);
        metaInfo.setSourceContent(m3u8Content);

        String relativeUrl = m3u8Url.substring(0, m3u8Url.lastIndexOf("/") + 1);

        M3u8Key m3u8Key = M3u8Key.newNonEncryptKey();
        String[] lineArr = m3u8Content.split("\\r\\n|\\n|\\r");
        for (int i = 0; i < lineArr.length; i++) {
            String line = lineArr[i];
            if (StrUtil.isBlank(line)) {
                continue;
            }
            if (line.startsWith("#") && !line.startsWith("#EXTINF") && !line.startsWith("#EXT-X-STREAM-INF")) {
                // 设置基本信息
                String[] arr = line.split(":", 2);
                String key = arr[0];
                String value = arr.length == 1 ? "" : arr[1];
                M3u8SpecEnum spec = M3u8SpecEnum.findByKey(key.replaceFirst("#", ""));
                Assert.notNull(spec, "not support such key: " + key + "  line=" + line);
                if (spec == M3u8SpecEnum.EXT_X_KEY || spec == M3u8SpecEnum.EXT_X_SESSION_KEY) {
                    m3u8Key = parseKey(value);
                    M3u8Key old = metaInfo.getOrPut(m3u8Key);
                    if (old != null) {
                        m3u8Key = old;
                    }
                    // uri -》url
                    if (StrUtil.isNotBlank(m3u8Key.getUri())) {
                        String m3u8KeyUrl = StrUtil.startWith(m3u8Key.getUri(), "http") ? m3u8Key.getUri() : mergeUrl(relativeUrl, m3u8Key.getUri());
                        m3u8Key.setUrl(m3u8KeyUrl);
                    }
                } else {
                    spec.getValueSetter().accept(metaInfo, value);
                }
            } else if (line.startsWith("#EXTINF")) {
                // 处理 TS 片段
                String duration = line.split(":")[1].replace(",", "");
                // 下一行
                i++;
                line = lineArr[i];
                String tsUrl = StrUtil.startWith(line, "http") ? line : mergeUrl(relativeUrl, line);
                if (CollectionUtil.isEmpty(forbiddenPartUrls) || !forbiddenPartUrls.contains(tsUrl)) {
                    M3u8TsPart part = new M3u8TsPart();
                    part.setKey(m3u8Key);
                    part.setDuration(duration);
                    part.setUrl(tsUrl);
                    part.setPartIndex(i);
                    metaInfo.addTsPart(part);
                }
            } else if (line.startsWith("#EXT-X-STREAM-INF")) {
                // 引用其他 m3u8
                Map<String, String> m = convertToKvMap(line.split(":", 2)[1]);
                // 下一行
                i++;
                line = lineArr[i];
                M3u8MetaInfo subInfo = new M3u8MetaInfo();
                subInfo.setUrl(mergeUrl(relativeUrl, line));
                subInfo.setExtStreamInf(m);
                metaInfo.addSubM3u8MetaInfo(subInfo);
            } else {
                throw new RuntimeException("unknown Line: " + line);
            }

        }
        return metaInfo;

    }

    private static String mergeUrl(String baseUrl, String urlSubfix) {
//        if(StrUtil.isBlank(urlSubfix)) {
//            return baseUrl;
//        }
        if (urlSubfix.startsWith("/"))
            urlSubfix = urlSubfix.replaceFirst("/", "");
        int position = 0;
        String subEnd, tempEnd = urlSubfix;
        while ((position = urlSubfix.indexOf("/", position)) != -1) {
            // 比较每一段 / 之间的内容
            subEnd = urlSubfix.substring(0, position + 1);
            if (baseUrl.endsWith(subEnd)) {
                tempEnd = urlSubfix.replaceFirst(subEnd, "");
                break;
            }
            ++position;
        }
        return baseUrl + tempEnd;
    }

    public static Map<String, String> convertToKvMap(String kvStr) {
        String[] kvArr = kvStr.split(",");
        Map<String, String> map = new HashMap<>(kvArr.length);
        for (String kv : kvArr) {
            String[] arr = kv.split("=", 2);
            map.put(arr[0], arr[1]);
        }
        return map;
    }

    public static M3u8Key parseKey(String c) {
        Map<String, String> map = convertToKvMap(c);
        String uri = Optional.ofNullable(map.get("URI")).map(u -> u.replace("\"", "")).orElse(null);

        M3u8Key m3u8Key = new M3u8Key();
        m3u8Key.setIvStr(map.get("IV"));
        m3u8Key.setMethod(map.get("METHOD"));
        m3u8Key.setUri(uri);
        return m3u8Key;
    }

}
